﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.IO;
using System.Security.Cryptography.Xml;
using System.Xml.Linq;

namespace DVK.SP2007.UniversalClient
{
    /// <summary>
    /// Encapsulates the DigiDoc file data structure and logic related to it.
    /// One DataFile element.
    /// </summary>
    public class DigiDocFileData
    {
        public const string DefaultContentType = "HASHCODE";
        public const string ContentTypeEmbeddedBase64 = "EMBEDDED_BASE64";
        public const string DefaultDataFileDataId = "D0";
        public const string DefaultDigestType = "SHA1";
        public const string DefaultDigiDocXmlNamespace = "http://www.sk.ee/DigiDoc/v1.3.0#";

        public string Id {get;set;}
        public string Filename {get;set;}
        public string MimeType { get; set; }
        public string ContentType { get; set; }
        public string DigestType { get; set; }
        public string DigestValue { get; set; }
        public string DataFieldData { get; set; }
        public int Size { get; set; }
        public byte[] Contents { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="DigiDocFileData"/> class.
        /// </summary>
        public DigiDocFileData()
        {
            Id = DefaultDataFileDataId;
            DigestType = DefaultDigestType;
            ContentType = DefaultContentType;
        }


        /// <summary>
        /// Gets the data file in xml.
        /// </summary>
        /// <returns></returns>
        public string GetDataFileXml()
        {
            string inputString = Convert.ToBase64String(this.Contents);
            StringBuilder encodedSB = new StringBuilder(inputString.Length + inputString.Length / 64 + 512);

            //we break b64encoded string into 64-char lines
            for (int i = 0; i < inputString.Length; i += 64)
            {
                encodedSB.Append(inputString.Substring(i, Math.Min(64, inputString.Length - i)));
                encodedSB.Append('\x0A');
            }

            encodedSB.Insert(
                        0,
                        string.Format(
                            "<DataFile ContentType=\"EMBEDDED_BASE64\" Filename=\"{0}\" Id=\"{1}\" MimeType=\"{2}\" Size=\"{3}\" xmlns=\"http://www.sk.ee/DigiDoc/v1.3.0#\" >",
                            this.Filename,
                            this.Id,
                            this.MimeType,
                            this.Size
                            ));

            encodedSB.Append("</DataFile>");
            encodedSB.Append('\x0A');

            return encodedSB.ToString();
        }
        /// <summary>
        /// Finds the MIME from data.
        /// </summary>
        /// <param name="pBC">The p BC.</param>
        /// <param name="pwzUrl">The PWZ URL.</param>
        /// <param name="pBuffer">The p buffer.</param>
        /// <param name="cbSize">Size of the cb.</param>
        /// <param name="pwzMimeProposed">The PWZ MIME proposed.</param>
        /// <param name="dwMimeFlags">The dw MIME flags.</param>
        /// <param name="ppwzMimeOut">The PPWZ MIME out.</param>
        /// <param name="dwReserved">The dw reserved.</param>
        /// <returns></returns>
        [DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
        static extern int FindMimeFromData(IntPtr pBC,
            [MarshalAs(UnmanagedType.LPWStr)] string pwzUrl,
            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)]
            byte[] pBuffer, int cbSize,
            [MarshalAs(UnmanagedType.LPWStr)]  string pwzMimeProposed,
              int dwMimeFlags, out IntPtr ppwzMimeOut, int dwReserved);
        /// <summary>
        /// MIMEs the type from.
        /// </summary>
        /// <param name="dataBytes">The data bytes.</param>
        /// <param name="mimeProposed">The MIME proposed.</param>
        /// <returns></returns>
        public static string MimeTypeFrom(byte[] dataBytes, string mimeProposed)
        {
            if (dataBytes == null) throw new ArgumentNullException("dataBytes");
            string mimeRet = String.Empty;
            IntPtr suggestPtr = IntPtr.Zero, filePtr = IntPtr.Zero, outPtr = IntPtr.Zero;
            if (mimeProposed != null && mimeProposed.Length > 0)
                mimeRet = mimeProposed;
            int ret = FindMimeFromData(IntPtr.Zero, null, dataBytes, dataBytes.Length, mimeProposed, 0, out outPtr, 0);
            if (ret == 0 && outPtr != IntPtr.Zero)
                return Marshal.PtrToStringUni(outPtr);
            return mimeRet;
        }

        
        /// <summary>
        /// Computes the SH a1 hash.
        /// <remarks>
        /// calculate SHA1 over canonic form of DataFile tag
        /// </remarks>
        /// </summary>
        /// <param name="fileDat">The file dat.</param>
        /// <returns></returns>
        public static string ComputeSHA1Hash(DigiDocFileData fileDat)
        {
            var xmldoc = XElement.Parse(fileDat.GetDataFileXml());
            XmlDsigC14NTransform xcanonical1 = new XmlDsigC14NTransform();
            xcanonical1.LoadInput(xmldoc);
            Stream stream = (Stream)xcanonical1.GetOutput(typeof(Stream));
            StreamReader rd = new StreamReader(stream);
            SHA1 sha1 = SHA1.Create();
            byte[] hash = sha1.ComputeHash((new UTF8Encoding()).GetBytes(rd.ReadToEnd()));
            return Convert.ToBase64String(hash);
        }
    }
}
